package com.study.spring_ai.config;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.study.spring_ai.tool.DateTimeTools;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.model.SimpleApiKey;
import org.springframework.ai.model.ollama.autoconfigure.OllamaChatProperties;
import org.springframework.ai.model.ollama.autoconfigure.OllamaInitializationProperties;
import org.springframework.ai.model.openai.autoconfigure.OpenAIAutoConfigurationUtil;
import org.springframework.ai.model.openai.autoconfigure.OpenAiChatProperties;
import org.springframework.ai.model.openai.autoconfigure.OpenAiConnectionProperties;
import org.springframework.ai.model.tool.DefaultToolExecutionEligibilityPredicate;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionEligibilityPredicate;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.management.ModelManagementOptions;
import org.springframework.ai.ollama.management.PullModelStrategy;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor;
import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Objects;

@Configuration
@EnableConfigurationProperties({OllamaChatProperties.class, OllamaInitializationProperties.class, OpenAiConnectionProperties.class, OpenAiChatProperties.class})
class Config {

    @Bean
    ChatClient voicechatClient(ChatClient.Builder builder) {
        return builder
                .defaultSystem("你是个友好的机器人，使用 {voice}的说话方式")
                .defaultAdvisors(new SimpleLoggerAdvisor())
//                .defaultTools(new DateTimeTools())
                .build();
    }

    /**
     * 默认处理工具执行异常,可以通过bean生命自定义异常处理
     */
    @Bean
    @ConditionalOnMissingBean
    ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
        return new DefaultToolExecutionExceptionProcessor(false);
    }


    @Bean
    ChatClient deepSeekChatClient(OpenAiConnectionProperties commonProperties,
                                  OpenAiChatProperties chatProperties,
                                  ToolCallingManager toolCallingManager,
                                  ObjectProvider<RestClient.Builder> restClientBuilderProvider,
                                  ObjectProvider<WebClient.Builder> webClientBuilderProvider,
                                  RetryTemplate retryTemplate,
                                  ResponseErrorHandler responseErrorHandler,
                                  ObjectProvider<ObservationRegistry> observationRegistry,
                                  ObjectProvider<ChatModelObservationConvention> observationConvention,
                                  ObjectProvider<ToolExecutionEligibilityPredicate> openAiToolExecutionEligibilityPredicate) {

        OpenAiApi openAiApi = this.openAiApi(chatProperties, commonProperties, (RestClient.Builder) restClientBuilderProvider.getIfAvailable(RestClient::builder), (WebClient.Builder) webClientBuilderProvider.getIfAvailable(WebClient::builder), responseErrorHandler, "chat");
        return ChatClient.builder(OpenAiChatModel
                .builder()
                .retryTemplate(retryTemplate)
                .toolCallingManager(toolCallingManager)
                .observationRegistry((ObservationRegistry)observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
                .toolExecutionEligibilityPredicate((ToolExecutionEligibilityPredicate)openAiToolExecutionEligibilityPredicate.getIfUnique(DefaultToolExecutionEligibilityPredicate::new))
                .openAiApi(openAiApi)
                .defaultOptions(chatProperties.getOptions())
                .build()).build();


    }

    private OpenAiApi openAiApi(OpenAiChatProperties chatProperties, OpenAiConnectionProperties commonProperties, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder, ResponseErrorHandler responseErrorHandler, String modelType) {
        OpenAIAutoConfigurationUtil.ResolvedConnectionProperties resolved = OpenAIAutoConfigurationUtil.resolveConnectionProperties(commonProperties, chatProperties, modelType);
        return OpenAiApi.builder().baseUrl(resolved.baseUrl()).apiKey(new SimpleApiKey(resolved.apiKey())).headers(resolved.headers()).completionsPath(chatProperties.getCompletionsPath()).embeddingsPath("/v1/embeddings").restClientBuilder(restClientBuilder).webClientBuilder(webClientBuilder).responseErrorHandler(responseErrorHandler).build();
    }


}